/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package manager;

import data.Card;
import data.Tool;
import data.Vote;
import data.VotingData;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import server.votesending.shamirsecret.GenerateKeysAndSecret;

/**
 *
 * @author Agnieszka
 */
public class ServerTool extends Tool {

    private BigInteger n;
    private BigInteger e;
    private BigInteger d;
    private Map<String,VotingData> listVotingData = new HashMap<String, VotingData>();
    Random generator = new Random(); 
    
    public ServerTool() {
        try {
            GenerateKeysAndSecret g=new GenerateKeysAndSecret();
            g.restoreKeysX509("key", "08976707138159620063");
            n=g.getPrivateKey().getModulus();
            e=g.getPublicKey().getPublicExponent();
            d=g.getPrivateKey().getPrivateExponent();
    //        try {
    //            RSAPublicKey pubKey;
    //            RSAPrivateKey privKey;
    //            //generate the RSA key pair
    //            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
    //            //initialise the KeyGenerator with a random number.
    //            keyGen.initialize(1024, new SecureRandom());
    //            KeyPair keypair = keyGen.genKeyPair();
    //            privKey = (RSAPrivateKey) keypair.getPrivate();
    //            pubKey = (RSAPublicKey) keypair.getPublic();
    //
    //            e = pubKey.getPublicExponent();
    //            d = privKey.getPrivateExponent();
    //            n = pubKey.getModulus();
    //        } catch (NoSuchAlgorithmException ex) {
    //            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
    //        }
    //        }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeySpecException ex) {
            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InterruptedException ex) {
            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(ServerTool.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public VotingData getVotingData(String name) {
        return listVotingData.get(name);
    }

    public VotingData addVotingData(VotingData vdata) {
        return listVotingData.put(vdata.getName(), vdata);
    }
    
    public void removeVotingData(String name) {
        listVotingData.remove(name);
    }
    
    

    /**
     * 
     * @return  skipped card number
     */
    public Integer randomCard(String name) {
        //Integer g = generator.nextInt(listVotingData.get(name).getCards().size());
        // Powyższe nie działa, zawsze zwraca ostatnią liczbę
        Integer g = new Random().nextInt(listVotingData.get(name).getCards().size());

        Card card = listVotingData.get(name).getCards().get(g.intValue());

        System.out.println("random card " + g + " " + card.getNumber() + " size " + listVotingData.get(name).getCards().size());

        return card.getNumber();
    }

    public Boolean verifyBlind(VotingData votingData, String name) throws UnsupportedEncodingException, Exception {
        listVotingData.get(name).prepareR(votingData.getCards());
        for (Card c : listVotingData.get(name).getCards()) {
            System.out.println("card " + c.getNumber());
            String first = null;
            for (Vote v : c.getVotes()) {
                if (first == null) {
                    first = new String(Tool.unblindToClean(v.getBlindVote(), e, n, c.getR()).toByteArray()).split(";")[1];
                } else {
                    String next = new String(Tool.unblindToClean(v.getBlindVote(), e, n, c.getR()).toByteArray()).split(";")[1];
                    if (!first.equals(next)) {
                        return Boolean.FALSE;
                    }
                }

            }

        }

        return Boolean.TRUE;
    }

    public Card signCard(String name) throws UnsupportedEncodingException {
        for (Vote v : listVotingData.get(name).getSkipCard().getVotes()) {
            System.out.print(".");
            v.setBlindSignVote(Tool.sign(v.getBlindVote(), d, n));
        }

        return listVotingData.get(name).getSkipCard();
    }
    
    public Card signCard(Card c) throws UnsupportedEncodingException {
        for (Vote v : c.getVotes()) {
            System.out.print(".");
            v.setBlindSignVote(Tool.sign(v.getBlindVote(), d, n));
        }

        return c;
    }

    public void verifyVote(Vote signVote) throws UnsupportedEncodingException {
        Tool.verify(signVote.getSignUnblindVote(), signVote.getHashVote(), d, n);

        Tool.verifyByRSA(signVote.getSignUnblindVote(), signVote.getHashVote(), e, n);
    }

    /**
     * 
     * @return  e - public key exponent
     */
    public BigInteger getE() {
        return e;
    }

    /**
     * 
     * @return n - public key modulus
     */
    public BigInteger getN() {
        return n;
    }

    public BigInteger getD() {
        return this.d;
    }
}
